"""
WebSocket数据缓存机制
====================

实现高性能的WebSocket数据缓存：
1. 多级缓存（内存 + Redis）
2. 智能缓存策略
3. 缓存预热和失效
4. 数据一致性保证
"""

import asyncio
import logging
from typing import Dict, Any, Optional, List, Set
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
import json
import hashlib

logger = logging.getLogger(__name__)


class CacheLevel(Enum):
    """缓存级别"""
    L1_MEMORY = "l1_memory"  # 一级缓存：内存
    L2_REDIS = "l2_redis"    # 二级缓存：Redis
    BOTH = "both"            # 两级缓存


class CacheStrategy(Enum):
    """缓存策略"""
    LRU = "lru"          # 最近最少使用
    LFU = "lfu"          # 最不经常使用
    TTL = "ttl"          # 基于时间
    ADAPTIVE = "adaptive"  # 自适应


@dataclass
class CacheEntry:
    """缓存条目"""
    key: str
    data: Any
    created_at: datetime = field(default_factory=datetime.now)
    last_accessed: datetime = field(default_factory=datetime.now)
    access_count: int = 0
    ttl_seconds: Optional[int] = None
    version: int = 1
    
    def is_expired(self) -> bool:
        """检查是否过期"""
        if self.ttl_seconds is None:
            return False
        
        elapsed = (datetime.now() - self.created_at).total_seconds()
        return elapsed > self.ttl_seconds
    
    def access(self):
        """访问缓存"""
        self.last_accessed = datetime.now()
        self.access_count += 1
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "key": self.key,
            "data": self.data,
            "created_at": self.created_at.isoformat(),
            "last_accessed": self.last_accessed.isoformat(),
            "access_count": self.access_count,
            "ttl_seconds": self.ttl_seconds,
            "version": self.version
        }


class MemoryCache:
    """
    内存缓存（L1缓存）
    
    特点：
    - 极快的访问速度
    - 有限的容量
    - 支持LRU/LFU策略
    """
    
    def __init__(
        self,
        max_size: int = 10000,
        default_ttl: int = 300,
        strategy: CacheStrategy = CacheStrategy.LRU
    ):
        """
        初始化内存缓存
        
        Args:
            max_size: 最大缓存条目数
            default_ttl: 默认TTL（秒）
            strategy: 缓存策略
        """
        self.max_size = max_size
        self.default_ttl = default_ttl
        self.strategy = strategy
        
        # 缓存存储
        self.cache: Dict[str, CacheEntry] = {}
        
        # 统计信息
        self.hits = 0
        self.misses = 0
        self.evictions = 0
        
        logger.info(
            f"MemoryCache initialized: max_size={max_size}, "
            f"ttl={default_ttl}s, strategy={strategy.value}"
        )
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存数据
        
        Args:
            key: 缓存键
        
        Returns:
            缓存的数据，如果不存在返回None
        """
        entry = self.cache.get(key)
        
        if entry is None:
            self.misses += 1
            return None
        
        # 检查是否过期
        if entry.is_expired():
            del self.cache[key]
            self.misses += 1
            return None
        
        # 更新访问信息
        entry.access()
        self.hits += 1
        
        return entry.data
    
    async def set(
        self,
        key: str,
        data: Any,
        ttl: Optional[int] = None
    ) -> bool:
        """
        设置缓存数据
        
        Args:
            key: 缓存键
            data: 数据
            ttl: TTL（秒），None表示使用默认值
        
        Returns:
            是否成功
        """
        # 检查容量
        if len(self.cache) >= self.max_size and key not in self.cache:
            await self._evict()
        
        # 创建缓存条目
        entry = CacheEntry(
            key=key,
            data=data,
            ttl_seconds=ttl if ttl is not None else self.default_ttl
        )
        
        self.cache[key] = entry
        
        return True
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 缓存键
        
        Returns:
            是否成功删除
        """
        if key in self.cache:
            del self.cache[key]
            return True
        return False
    
    async def clear(self):
        """清空缓存"""
        self.cache.clear()
    
    async def _evict(self):
        """驱逐缓存条目"""
        if not self.cache:
            return
        
        # 根据策略选择驱逐的条目
        if self.strategy == CacheStrategy.LRU:
            # 最近最少使用
            victim_key = min(
                self.cache.keys(),
                key=lambda k: self.cache[k].last_accessed
            )
        elif self.strategy == CacheStrategy.LFU:
            # 最不经常使用
            victim_key = min(
                self.cache.keys(),
                key=lambda k: self.cache[k].access_count
            )
        else:
            # 默认LRU
            victim_key = min(
                self.cache.keys(),
                key=lambda k: self.cache[k].last_accessed
            )
        
        del self.cache[victim_key]
        self.evictions += 1
    
    def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        total_requests = self.hits + self.misses
        hit_rate = self.hits / total_requests if total_requests > 0 else 0
        
        return {
            "size": len(self.cache),
            "max_size": self.max_size,
            "hits": self.hits,
            "misses": self.misses,
            "hit_rate": round(hit_rate * 100, 2),
            "evictions": self.evictions,
            "utilization": round(len(self.cache) / self.max_size * 100, 2)
        }


class WebSocketDataCache:
    """
    WebSocket数据缓存
    
    多级缓存架构：
    - L1: 内存缓存（极快）
    - L2: Redis缓存（快速且持久）
    
    功能：
    - 智能缓存策略
    - 数据版本控制
    - 缓存预热
    - 一致性保证
    """
    
    def __init__(
        self,
        enable_l1: bool = True,
        enable_l2: bool = False,
        l1_max_size: int = 10000,
        l1_ttl: int = 300,
        l2_ttl: int = 3600,
        cache_strategy: CacheStrategy = CacheStrategy.LRU
    ):
        """
        初始化数据缓存
        
        Args:
            enable_l1: 是否启用L1缓存
            enable_l2: 是否启用L2缓存（Redis）
            l1_max_size: L1缓存最大条目数
            l1_ttl: L1缓存默认TTL（秒）
            l2_ttl: L2缓存默认TTL（秒）
            cache_strategy: 缓存策略
        """
        self.enable_l1 = enable_l1
        self.enable_l2 = enable_l2
        self.l2_ttl = l2_ttl
        
        # L1缓存（内存）
        self.l1_cache: Optional[MemoryCache] = None
        if enable_l1:
            self.l1_cache = MemoryCache(
                max_size=l1_max_size,
                default_ttl=l1_ttl,
                strategy=cache_strategy
            )
        
        # L2缓存（Redis）
        self.l2_cache = None  # 如果需要Redis，这里可以初始化
        
        # 数据版本管理
        self.data_versions: Dict[str, int] = {}
        
        # 缓存预热列表
        self.preheat_keys: Set[str] = set()
        
        logger.info(
            f"WebSocketDataCache initialized: l1={enable_l1}, l2={enable_l2}, "
            f"l1_size={l1_max_size}, l1_ttl={l1_ttl}s"
        )
    
    async def get(
        self,
        key: str,
        use_l1: bool = True,
        use_l2: bool = True
    ) -> Optional[Any]:
        """
        获取缓存数据（多级查询）
        
        查询顺序：L1 -> L2
        
        Args:
            key: 缓存键
            use_l1: 是否使用L1缓存
            use_l2: 是否使用L2缓存
        
        Returns:
            缓存的数据
        """
        # 1. 尝试L1缓存
        if use_l1 and self.l1_cache:
            data = await self.l1_cache.get(key)
            if data is not None:
                logger.debug(f"L1 cache hit: {key}")
                return data
        
        # 2. 尝试L2缓存
        if use_l2 and self.l2_cache:
            # 这里可以添加Redis查询逻辑
            pass
        
        return None
    
    async def set(
        self,
        key: str,
        data: Any,
        ttl: Optional[int] = None,
        level: CacheLevel = CacheLevel.BOTH,
        version: Optional[int] = None
    ) -> bool:
        """
        设置缓存数据
        
        Args:
            key: 缓存键
            data: 数据
            ttl: TTL（秒）
            level: 缓存级别
            version: 数据版本
        
        Returns:
            是否成功
        """
        # 更新版本
        if version is not None:
            self.data_versions[key] = version
        else:
            self.data_versions[key] = self.data_versions.get(key, 0) + 1
        
        # 写入L1缓存
        if level in [CacheLevel.L1_MEMORY, CacheLevel.BOTH] and self.l1_cache:
            await self.l1_cache.set(key, data, ttl)
        
        # 写入L2缓存
        if level in [CacheLevel.L2_REDIS, CacheLevel.BOTH] and self.l2_cache:
            # 这里可以添加Redis写入逻辑
            pass
        
        return True
    
    async def delete(self, key: str) -> bool:
        """删除缓存"""
        success = True
        
        # 删除L1缓存
        if self.l1_cache:
            await self.l1_cache.delete(key)
        
        # 删除L2缓存
        if self.l2_cache:
            # 这里可以添加Redis删除逻辑
            pass
        
        # 删除版本信息
        self.data_versions.pop(key, None)
        
        return success
    
    async def invalidate_pattern(self, pattern: str):
        """
        按模式失效缓存
        
        Args:
            pattern: 键模式（如 "symbol:*"）
        """
        # 这里可以实现模式匹配和批量删除
        pass
    
    async def preheat(self, keys: List[str], data_loader: callable):
        """
        缓存预热
        
        Args:
            keys: 要预热的键列表
            data_loader: 数据加载函数
        """
        logger.info(f"Preheating cache for {len(keys)} keys")
        
        for key in keys:
            try:
                data = await data_loader(key)
                if data is not None:
                    await self.set(key, data)
                    self.preheat_keys.add(key)
            except Exception as e:
                logger.error(f"Error preheating key {key}: {e}")
        
        logger.info(f"Cache preheat completed: {len(self.preheat_keys)} keys loaded")
    
    def get_version(self, key: str) -> int:
        """获取数据版本"""
        return self.data_versions.get(key, 0)
    
    def is_stale(self, key: str, expected_version: int) -> bool:
        """
        检查数据是否过期
        
        Args:
            key: 缓存键
            expected_version: 期望的版本号
        
        Returns:
            是否过期
        """
        current_version = self.get_version(key)
        return current_version < expected_version
    
    async def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        stats = {
            "enable_l1": self.enable_l1,
            "enable_l2": self.enable_l2,
            "preheat_keys": len(self.preheat_keys),
            "data_versions": len(self.data_versions)
        }
        
        # L1统计
        if self.l1_cache:
            stats["l1_stats"] = self.l1_cache.get_stats()
        
        # L2统计
        if self.l2_cache:
            # 这里可以添加Redis统计
            pass
        
        return stats
    
    async def clear_all(self):
        """清空所有缓存"""
        if self.l1_cache:
            await self.l1_cache.clear()
        
        if self.l2_cache:
            # 这里可以添加Redis清空逻辑
            pass
        
        self.data_versions.clear()
        self.preheat_keys.clear()
        
        logger.info("All caches cleared")


# 全局缓存实例
_cache: Optional[WebSocketDataCache] = None


def get_websocket_cache(**kwargs) -> WebSocketDataCache:
    """获取全局缓存实例"""
    global _cache
    if _cache is None:
        _cache = WebSocketDataCache(**kwargs)
    return _cache

